package asteroids;

import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Immutable;


public class FlyingObject {
	protected Position pos;
	protected Velocity speed;
	public final static double minSize = 0;
	protected final double size;
	protected World world;
	protected final double mass;

	/**
	 * 
	 * @param position
	 * @param velocity
	 * @param size
	 * @param mass
	 * @post | (new this).getMass() == mass
	 * @post | (new this).getVelocity() == velocity
	 * @post | (new this).getPosition() == position
	 * @post | (new this).getSize() == size
	 * @invar | this.getSize() == size && isValidSize(this.getSize())
	 * @invar | this.getMass() == mass && this.getMass > O
	 * @throws IllegalArgumentException
	 *             | position < 0
	 * @throws NullPointerException
	 *             | position == null
	 * @throws IllegalArgumentException
	 *             | !isValidSize(size)
	 * 
	 */
	public FlyingObject(Position position, Velocity velocity, double size,
			double mass) {
		if (mass < 0) {
			throw new IllegalArgumentException();
		}
		this.mass = mass;
		if (position == null) {
			throw new NullPointerException();
		}
		if (velocity == null) {
			velocity = new Velocity(0, 0);
		}

		this.pos = position;
		this.speed = velocity;

		if (!isValidSize(size)) {
			throw new IllegalArgumentException();
		}
		this.size = size;
	}

	/**
	 * @result | (new this).getWorld() == world
	 * @throws IllegalArgumentException
	 *             | world == null || !world.canBeAdded(this)
	 */
	public void setWorld(World world) throws IllegalArgumentException {
		if ((world == null) || !world.canBeAdded(this))
			throw new IllegalArgumentException();
		this.world = world;
	}

	/**
	 * 
	 * @return | result == this.minSize
	 */
	@Basic
	@Immutable
	public static double getMinimumSize() {
		return minSize;
	}

	/**
	 * 
	 * @return | result == this.mass
	 */
	@Basic
	@Immutable
	public double getMass() {
		return this.mass;
	}

	/**
	 * 
	 * @return | result == this.world
	 */
	@Immutable
	@Basic
	public World getWorld() {
		return this.world;
	}

	/**
	 * 
	 * @return | result == this.pos
	 */
	@Basic
	public Position getPosition() {
		return this.pos;
	}

	/**
	 * 
	 * @param position
	 * @post |(new this).getPosition() == position
	 * @throws NullPointerException
	 *             |position == null
	 */
	@Basic
	public void setPosition(Position position) throws NullPointerException {
		if (position == null) {
			throw new NullPointerException();
		} else {
			this.pos = position;
		}

	}

	/**
	 * 
	 * @return |result == this.speed
	 */
	@Basic
	public Velocity getVelocity() {
		return this.speed;
	}

	/**
	 * 
	 * @param velocity
	 * @post (new this).getVelocity == velocity
	 */
	@Basic
	public void setVelocity(Velocity velocity) {
		if (velocity != null)
			this.speed = velocity;
	}

	/**
	 * 
	 * @return |result == this.size
	 */
	@Immutable
	@Basic
	public double getSize() {
		return this.size;
	}

	/**
	 * 
	 * @param size
	 * @return | result == ((size >= minSize) && (size >= 0) &&
	 *         |!(Double.isNaN(size))
	 */
	@Immutable
	public static boolean isValidSize(double size) {
		return ((size >= minSize) && (size >= 0) && !(Double.isNaN(size)));
	}

	/**
	 * @post |(new this).getVelocity().getX() == -this.getVelocity().getX()
	 */
	public void BounceX() {
		double newX = (-1) * (this.getVelocity().getX());
		Velocity newVel = new Velocity(newX, this.getVelocity().getY());
		this.setVelocity(newVel);
	}

	/**
	 * @post |(new this).getVelocity().getY() == -this.getVelocity().getY()
	 */
	public void BounceY() {
		double newY = (-1) * (this.getVelocity().getY());
		Velocity newVel = new Velocity(this.getVelocity().getX(), newY);
		this.setVelocity(newVel);
	}

	/**
	 * 
	 * @param thing
	 * @return | result == ( (thing == this)||( getDistanceBetween(this, thing)
	 *         < 0) )
	 * @throws NullPointerException
	 *             | thing == null
	 */
	public boolean overlap(FlyingObject thing) throws NullPointerException {
		if (thing == null) {
			throw new NullPointerException();
		}

		else if (thing == this) {
			return true;
		} else {
			return (this.getDistanceBetween(thing) < 0);
		}

	}

	/**
	 * 
	 * @param thing1
	 * @param thing2
	 * @return | thing1 == thing2
	 * @return |result == Vector.getDistancebetween(thing1,thing2)
	 * @throws NullPointerException
	 *             |(thing1 == null) || (thing2 == null)
	 */
	public double getDistanceBetween(FlyingObject thing1) throws NullPointerException {
		double value = Vector.getDistanceBetween(thing1, this);
		return value;
	}

	/**
	 * 
	 * @param thing1
	 * @param thing2
	 * @return |result == POSITIVE_INFINITY && getTimeToCollision(thing1,
	 *         thing2) == POSITIVE_INFINITY
	 * @return |result == Vector.getCollisionPosition(thing1,thing2)
	 * @throws NullPointerException
	 *             |thing1 == null || thing2 == null
	 */
	public Position getCollisionPosition(FlyingObject thing1) throws NullPointerException {

		 
		return Vector.getCollisionPosition(thing1,this);

	}

	/**
	 * 
	 * @param deltaT
	 * @post |(new this).getPosition().getX == (this.getPosition().getX +
	 *       |deltaT*this.getVelocity.getX) (new this).getPosition().getY() ==
	 *       |(this.getPosition().getY() + deltaT*this.getVelocity.getY)
	 * @throws IllegalArgumentException
	 *             |(deltaT < 0) || (Double.isNaN(deltaT))
	 */
	public void move(double deltaT) throws IllegalArgumentException {
		Position newPos = Vector.move(deltaT, this.getPosition(),
				this.getVelocity());
		this.setPosition(newPos);
	}

	/**
	 * 
	 * @param thing1
	 * @param thing2
	 * @return |result == Vector.getTimeToCollision(thing1, thing2)
	 * @throws NullPointerException
	 *             |(thing1==null ||thing2==null)
	 */
	public static double getTimeToCollision(FlyingObject thing1,
			FlyingObject thing2) throws NullPointerException {
		double result = Vector.getTimeToCollision(thing1, thing2);
		return result;

	}

}
